iT邦幫忙

2025 iThome 鐵人賽

DAY 29
1

在軟體開發與維運的漫漫長路中,效能問題如同一隻隻難以捉摸的幽靈,時而顯現,時而隱匿。傳統的效能分析(Profiling)方法,往往像是在特定時間點拍攝一張快照,雖能捕捉到瞬間的狀態,卻難以洞察在真實生產環境中,那些與用戶負載、時間推移相關的效能瓶頸。為了解決這個困境,持續性效能分析(Continuous Profiling) 的理念應運而生。

它主張對應用程式進行全天候、低開銷的效能監控,將無數個效能快照匯集成一部紀錄片,讓我們得以隨時回溯,精準定位任何時間點的效能瓶頸。Grafana Pyroscope 正是實現此理念的佼佼者,它是一個開源的持續性效能分析平台,能持續收集、儲存並以火焰圖(Flame Graph) 的形式,直觀地視覺化應用程式的效能數據。

今天,我們將從零開始,親手搭建一個完整的實驗環境,感受 Pyroscope 如何為我們揭示程式碼的效能秘密。

建立我們的實驗品:一個 Python 應用

首先,我們需要一個「被觀察」的對象。這裡,我們編寫一個簡單的 Python 應用程式,它會模擬不同的工作負載。建立一個名為 main.py 的檔案,並填入以下內容:

import os
import time
import threading
import pyroscope

# 從環境變數初始化 Pyroscope
pyroscope.configure(
    application_name="my.python.app", # 應用名稱,將顯示在 Grafana 中
    server_address=os.environ.get("PYROSCOPE_SERVER_ADDRESS", "http://pyroscope:4040"),
    enable_logging=True,
)

def slow_function():
    """一個模擬耗時較長的操作"""
    time.sleep(0.1)

def fast_function():
    """一個模擬耗時較短的操作"""
    time.sleep(0.05)

def work(tags):
    """主要的工作負載,會呼叫其他函式"""
    # 使用 tag_wrapper 為這個執行緒的效能數據打上標籤
    with pyroscope.tag_wrapper(tags):
        while True:
            fast_function()
            slow_function()
            print(f"Task with tags {tags} finished a cycle.")
            time.sleep(1) # 每秒執行一次循環

if __name__ == "__main__":
    print("Starting Python application with continuous profiling...")
    # 建立並啟動一個執行緒,標記為 "fast-lane"
    threading.Thread(target=work, args=({"speed": "fast"},)).start()

    # 建立並啟動另一個執行緒,標記為 "slow-lane"
    threading.Thread(target=work, args=({"speed": "slow"},)).start()

這個腳本的精華在於 pyroscope.configure 初始化了分析工具,並透過 pyroscope.tag_wrapper 為不同的執行緒(我們用以模擬快、慢兩種任務)打上了 speed="fast"speed="slow" 的標籤。這將在後續的分析中展現出強大的威力。

為了讓這個 Python 應用能順利執行,我們還需要告訴 Python 它依賴哪些套件。建立一個名為 requirements.txt 的檔案,內容僅需一行:

pyroscope-io

搭建我們的觀測站:Docker Compose

接下來,我們使用 Docker Compose 來編排我們的觀測環境,它將包含 Pyroscope 伺服器、我們剛才設計的 Python 應用,以及用於視覺化的 Grafana。建立一個名為 docker-compose.yml 的檔案:

version: '3.8'

services:
  grafana:
    image: grafana/grafana:10.2.0
    ports:
      - "3000:3000"
    volumes:
      - ./grafana-provisioning/datasources:/etc/grafana/provisioning/datasources
    environment:
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
      - GF_AUTH_DISABLE_LOGIN_FORM=true
    networks:
      - loki

  pyroscope:
    image: grafana/pyroscope:1.1.0
    ports:
      - "4040:4040"
    networks:
      - loki

  app:
    image: python:3.9-slim
    volumes:
      - ./app:/app
    working_dir: /app
    command: pip install -r requirements.txt && python main.py
    environment:
      - PYROSCOPE_SERVER_ADDRESS=http://pyroscope:4040
    depends_on:
      - pyroscope
    networks:
      - loki

networks:
  loki:
    driver: bridge

這個設定檔定義了三個服務,並讓它們共享一個名為 loki 的網路,以便互相通訊。

自動化儀器校準:Grafana Provisioning

為了讓 Grafana 一啟動就能自動連接到 Pyroscope,我們利用 Grafana 的 Provisioning(自動化佈建)功能。我們需要建立一個設定檔,告訴 Grafana Pyroscope 伺服器的位置。

請建立如下的路徑與檔案:grafana-provisioning/datasources/datasource.yml,並填入以下內容:

apiVersion: 1

datasources:
  - name: Pyroscope
    type: grafana-pyroscope-datasource
    access: proxy
    url: http://pyroscope:4040
    isDefault: true
    version: 1
    editable: false

啟動實驗並分析結果

萬事俱備!現在,請將 main.pyrequirements.txt 放在名為 app 的子目錄中,並將 datasource.yml 放在 grafana-provisioning/datasources 的路徑下。你的目錄結構看起來應該像這樣:

. (你的工作目錄)
├── docker-compose.yml
├── app/
│   ├── main.py
│   └── requirements.txt
└── grafana-provisioning/
    └── datasources/
        └── datasource.yml

在你的工作目錄下,執行以下指令啟動所有服務:

docker-compose up -d

等待片刻,待 Docker 下載映像檔並啟動容器後,我們的實驗就正式開始了。

  1. 進入 Grafana 控制台:在瀏覽器中打開 http://localhost:3000
  2. 探索效能數據:在左側選單點擊「Explore」(指南針圖示)。你會發現 Grafana 已經自動選擇了「Pyroscope」作為資料來源。
  3. 檢視火焰圖:在「Profile Type」下拉選單中,選擇我們的應用 my.python.app,然後點擊「Run query」。螢幕上出現的,就是著名的「火焰圖」。圖中每個長條代表一個函式,長條的寬度與它佔用的 CPU 時間成正比。透過這張圖,slow_function 佔用了更多時間的秘密便一目了然。
  4. 發揮標籤的威力:在查詢欄位,你可以像使用 PromQL 一樣利用我們之前埋下的標籤。點擊「Label」並選擇 speed,然後在「Value」中選擇 fast。再次查詢,火焰圖便只會顯示 speed="fast" 那個執行緒的效能數據。切換到 slow,你就能精準比較兩種不同任務的效能開銷。

結語

恭喜你!今天,你不僅學會了如何搭建一個持續性效能分析環境,更親身體驗了如何像偵探一樣,利用 Pyroscope 和火焰圖,從程式碼的蛛絲馬跡中找出效能的真相。將這項技術融入你的開發流程,將賦予你一雙洞察系統瓶頸的火眼金睛。


上一篇
Day28 - Grafana 進階探索:打造互動式儀表板
下一篇
Day30 - 終點與起點:一趟從壓力測試到全方位可觀測性的旅程
系列文
Vibe Coding 後的挑戰:Locust x Loki 負載及監控30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言